
#include "chat_errno.h"
#include "chat_session.h"


CChatSession::CChatSession()
{
    m_arrMsgHandlers[CHAT_MSG_REGISTER] = std::bind(&CChatSession::__DoRegister, this, _1, _2, _3);
    m_arrMsgHandlers[CHAT_MSG_LOGIN] = std::bind(&CChatSession::__DoLogin, this, _1, _2, _3);
    m_arrMsgHandlers[CHAT_MSG_LOGOUT] = std::bind(&CChatSession::__DoLogut, this, _1, _2, _3);
    m_arrMsgHandlers[CHAT_MSG_CHAT] = std::bind(&CChatSession::__DoChat, this, _1, _2, _3);
}

int32_t CChatSession::Init()
{
    int32_t iErr = m_objSubPub.Init();
    if (iErr != CHAT_ERR_OK)
    {
        return iErr;
    }

    iErr = m_objPublisher.Init();
    if (iErr != CHAT_ERR_OK)
    {
        return iErr;
    }

    const CHAT_REDIS_CONFIG_S& refRedisCfg = CChatConfig::GetInstance().GetRedisConfig();
    iErr = m_objSubPub.Connect(refRedisCfg.strHost.c_str(), refRedisCfg.usPort);
    if (iErr != CHAT_ERR_OK)
    {
        printf("Failed to connect to redis server %s:%hu\n", refRedisCfg.strHost.c_str(),
               refRedisCfg.usPort);

        return iErr;
    }

    iErr = m_objPublisher.Connect(refRedisCfg.strHost.c_str(), refRedisCfg.usPort);
    if (iErr != CHAT_ERR_OK)
    {
        printf("Failed to connect to redis server %s:%hu\n", refRedisCfg.strHost.c_str(),
               refRedisCfg.usPort);

        return iErr;
    }

    return CHAT_ERR_OK;
}

int32_t CChatSession::RegisterUser(const char* pcName, const char* pcPassword)
{
    if (pcName == nullptr || *pcName == '\0' || pcPassword == nullptr || *pcPassword == '\0')
    {
        return CHAT_ERR_INVALID_PARAMETER;
    }

    printf("Handle user register request, User=%s\n", pcName);

    return m_objUserModel.Insert(pcName, pcPassword);
}

void CChatSession::OnConnect(const TcpConnectionPtr& refConnPtr)
{
    printf("Connection opened\n");
}

void CChatSession::OnMessage(const TcpConnectionPtr& refConnPtr, Buffer* pobjBuf, Timestamp objTS)
{
    const char* pcMsg = pobjBuf->peek();
    size_t ulMsgLen = pobjBuf->readableBytes();

    printf("Received message, length=%lu\n", ulMsgLen);
    if (ulMsgLen < sizeof(CHAT_MSG_HEAD_S))
    {
        printf("Received bad message!!!\n");

        return;
    }

    CHAT_MSG_HEAD_S* pstHead = (CHAT_MSG_HEAD_S*)pcMsg;

    pstHead->usType = htons(pstHead->usType);
    pstHead->usVersion = htons(pstHead->usVersion);
    pstHead->uiMsgLen = htonl(pstHead->uiMsgLen);

    if (pstHead->usType >= CHAT_MSG_MAX)
    {
        printf("Received bad message, type is unknown !!!\n");
        return;
    }
    if (m_arrMsgHandlers[pstHead->usType] != nullptr)
    {
        m_arrMsgHandlers[pstHead->usType](refConnPtr, *pstHead, objTS);
    }

    pobjBuf->retrieve(sizeof(CHAT_MSG_HEAD_S) + pstHead->uiMsgLen);
}

void CChatSession::__SendSetResponse(const TcpConnectionPtr& refConnPtr, int iMsgType, int iErrCode)
{
    char acBuf[256];

    CHAT_MSG_HEAD_S* pstHead = (CHAT_MSG_HEAD_S*)acBuf;
    pstHead->uiMsgLen = htonl(sizeof(CHAT_MSG_SET_RESPONSE_S));
    pstHead->usType = htons(iMsgType);
    pstHead->usVersion = htons(CHAT_VERSION);

    CHAT_MSG_SET_RESPONSE_S* pstBody = (CHAT_MSG_SET_RESPONSE_S*)(acBuf + sizeof(CHAT_MSG_HEAD_S));
    pstBody->iErrNo = htonl(iErrCode);

    Buffer objBuf;
    size_t ulLen = sizeof(CHAT_MSG_HEAD_S) + sizeof(CHAT_MSG_SET_RESPONSE_S);
    objBuf.append(acBuf, ulLen);

    printf("Send set response, %d\n", iErrCode);
    refConnPtr->send(&objBuf);
}

void CChatSession::__DoLogin(const TcpConnectionPtr &refConnPtr, const CHAT_MSG_HEAD_S& refMsgHead, Timestamp time)
{
    if (refMsgHead.uiMsgLen < sizeof(CHAT_MSG_USER_LOGIN_S))
    {
        printf("Error message\n");
        return;
    }

    CHAT_MSG_USER_LOGIN_S* pstUserLogin = (CHAT_MSG_USER_LOGIN_S*)((char *)&refMsgHead + sizeof(CHAT_MSG_HEAD_S));
    printf("User login request, user=%s\n", pstUserLogin->szUser);

    CChatUser objUser;
    int32_t iErr = m_objUserModel.Query(pstUserLogin->szUser, objUser);
    if (strcmp(objUser.GetName().c_str(), pstUserLogin->szUser) == 0 && 
        strcmp(objUser.GetPassword().c_str(), pstUserLogin->szPassword) == 0)
    {
        if (strcmp(objUser.GetState().c_str(), "offline") == 0)
        {
            iErr = CHAT_ERR_OK;
            objUser.SetState("online");
            iErr = m_objUserModel.UpdateState(objUser);
            if (iErr == CHAT_ERR_OK)
            {
                m_objSubPub.Subscribe(objUser.GetID());
                std::unique_lock<std::mutex> objUL(m_objConnMtx);
                m_mapUserConn[objUser.GetID()] = refConnPtr;
            }
        }
    }
    
    __SendSetResponse(refConnPtr, CHAT_MSG_LOGIN_RSP, iErr);
}

void CChatSession::__DoLogut(const TcpConnectionPtr &refConnPtr, const CHAT_MSG_HEAD_S& refMsgHead, Timestamp time)
{
    if (refMsgHead.uiMsgLen < sizeof(CHAT_MSG_USER_LOGOUT_S))
    {
        printf("Bad message\n");
        return;
    }

    CHAT_MSG_USER_LOGOUT_S* pstBody = (CHAT_MSG_USER_LOGOUT_S*)((char *)&refMsgHead + sizeof(CHAT_MSG_HEAD_S));
    printf("User logout request, user=%s\n", pstBody->szUser);

    CChatUser objUser;
    int32_t iErr = m_objUserModel.Query(pstBody->szUser, objUser);
    if (iErr == CHAT_ERR_OK && 
        strcmp(objUser.GetState().c_str(), "online") == 0)
    {
        objUser.SetState("offline");
        iErr = m_objUserModel.UpdateState(objUser);
        if (iErr == CHAT_ERR_OK)
        {
            std::unique_lock<std::mutex> objUL(m_objConnMtx);
            m_mapUserConn.erase(objUser.GetID());
        }
    }

    __SendSetResponse(refConnPtr, CHAT_MSG_LOGOUT_RSP, iErr);
}

void CChatSession::__DoRegister(const TcpConnectionPtr &refConnPtr, const CHAT_MSG_HEAD_S& refMsgHead, Timestamp time)
{
    if (refMsgHead.uiMsgLen < sizeof(CHAT_MSG_USER_REGISTER_S))
    {
        printf("Error message\n");
        return;
    }

    CHAT_MSG_USER_REGISTER_S* pstRegister = (CHAT_MSG_USER_REGISTER_S*)((char *)&refMsgHead + sizeof(CHAT_MSG_HEAD_S));
    printf("User register request, user=%s\n", pstRegister->szUser);

    int32_t iErr = m_objUserModel.Insert(pstRegister->szUser, pstRegister->szPassword);
    __SendSetResponse(refConnPtr, CHAT_MSG_REGISTER_RSP, iErr);
}

void CChatSession::__DoChat(const TcpConnectionPtr &refConnPtr, const CHAT_MSG_HEAD_S& refMsgHead, Timestamp time)
{
    if (refMsgHead.uiMsgLen < sizeof(CHAT_MSG_CHAT_S))
    {
        printf("Error message\n");
        return;
    }
    CHAT_MSG_CHAT_S* pstBody = (CHAT_MSG_CHAT_S*)((char*)&refMsgHead + sizeof(CHAT_MSG_HEAD_S));
    CChatUser objUser;
    int32_t iErr = m_objUserModel.Query(pstBody->szTo, objUser);
    if (iErr != CHAT_ERR_OK)
    {
        printf("User %s not exist\n", pstBody->szTo);
        return;
    }

    m_objPublisher.Publish(objUser.GetID(), pstBody->acData);
}